{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b46bdfce-8558-4db7-bf6c-e30409103a0c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "device(type='cuda')"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torchvision import transforms, datasets\n",
    "from torch.utils.data import DataLoader\n",
    "import matplotlib.pyplot as plt\n",
    "from tqdm import tqdm\n",
    "#隐藏警告\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")               #忽略警告信息\n",
    "plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号\n",
    "plt.rcParams['figure.dpi']         = 100        #分辨率\n",
    "\n",
    "\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "device"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb3202bf-034c-4a4e-8a39-89925e0007c5",
   "metadata": {},
   "source": [
    "### 读取（下载）MNIST数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5cc5c44b-a5f0-422d-bde6-d4d1c984f278",
   "metadata": {},
   "outputs": [],
   "source": [
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.5,), (0.5,))  # 归一化到[-1, 1]，更适合tanh激活函数\n",
    "])\n",
    "\n",
    "\"\"\"\n",
    "num_workers=4 : 使用 4 个子进程来并行加载数据，这可以加快数据加载速度\n",
    "pin_memory=True : 将加载的数据张量固定在内存, 数据加载到 GPU 的速度会更快, 通常在使用 GPU 训练时，将此参数设置为 True\n",
    "\"\"\"\n",
    "train_dataset = datasets.MNIST(root='../datasets/mnist', train=True, download=True, transform=transform)  # download=True:如果没有, 下载数据集\n",
    "test_dataset = datasets.MNIST(root='../datasets/mnist', train=False, download=True, transform=transform)  # train=True训练集，=False测试集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "12857fef-94c9-476a-b5e8-38b5a2b4105f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 增大batch_size以提高稳定性\n",
    "batch_size = 128\n",
    "\n",
    "batch_size = 128\n",
    "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4, pin_memory=True)\n",
    "test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b077b1f0-4933-4603-9de1-1ca9bdd53678",
   "metadata": {},
   "source": [
    "### 构建模型\n",
    "**Generator 和 Discriminator**\n",
    "\n",
    "[反卷积(Transposed conv deconv)实现原理（通俗易懂）](https://blog.csdn.net/weixin_39326879/article/details/120797857) : 在GAN中，生成器使用反卷积层将低维随机噪声转换为高分辨率图像"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0cb638e1-16ba-49d2-9df3-d988baad2961",
   "metadata": {},
   "source": [
    "#### 反卷积层（Deconvolution Layer）\n",
    "\n",
    "反卷积层，也称为**转置卷积层（Transposed Convolution Layer）**，是一种用于上采样的操作。它的作用是将低分辨率的特征图（feature map）转换为高分辨率的特征图。反卷积层在生成对抗网络（GAN）、图像分割、超分辨率等任务中非常常见。\n",
    "\n",
    "\n",
    "| 特性                | 卷积层（Convolution）                          | 反卷积层（Transposed Convolution）            |\n",
    "|---------------------|-----------------------------------------------|-----------------------------------------------|\n",
    "| **目的**            | 下采样，提取特征                              | 上采样，生成高分辨率特征图                    |\n",
    "| **输入与输出关系**  | 输入尺寸 > 输出尺寸                           | 输入尺寸 < 输出尺寸                           |\n",
    "| **计算方式**        | 通过滑动窗口和卷积核计算输出                  | 通过填充和卷积核的转置计算输出                |\n",
    "| **应用场景**        | 特征提取、分类、检测等                        | 图像生成、分割、超分辨率等                    |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "198d2b1d-7911-4d8c-bdad-c95b35e44396",
   "metadata": {},
   "source": [
    "#### Generator \n",
    "生成器的目标是从随机噪声（latent vector）和条件（condition）生成逼真的图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ee6ff0a6-5c40-40a0-9f45-4416398709c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Generator(nn.Module):\n",
    "    def __init__(self, input_dim=100, output_dim=1, class_num=10):\n",
    "        '''\n",
    "        初始化生成网络\n",
    "        :param input_dim:输入随机噪声的维度，（随机噪声是为了增加输出多样性）\n",
    "        :param output_dim:生成图像的通道数（灰度图为1，RGB图为3）\n",
    "        :param class_num:图像种类\n",
    "        '''\n",
    "        super(Generator, self).__init__()\n",
    "        \"\"\"\n",
    "         为什么需要拼接随机噪声和条件向量？\n",
    "         拼接随机噪声和条件向量的目的是将两种信息结合起来，作为生成器的输入：\n",
    "         随机噪声：提供生成数据的随机性。\n",
    "         条件向量：提供生成数据的条件信息。\n",
    "         通过拼接，生成器可以根据条件向量生成符合特定条件的数据, 同时确保每次生成的数据会有所不同\n",
    "         \"\"\"\n",
    "        self.input_dim = input_dim\n",
    "        self.class_num = class_num\n",
    "        self.output_dim = output_dim\n",
    "        \n",
    "        # 嵌入层处理条件向量(类别标签), 提高条件信息的表达能力\n",
    "        self.label_emb = nn.Embedding(class_num, class_num)\n",
    "        \n",
    "        # 全连接层，将输入向量映射到高维空间，然后通过反卷积层生成图像\n",
    "        self.fc = nn.Sequential(\n",
    "            nn.Linear(self.input_dim + self.class_num, 256),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Linear(256, 512),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Linear(512, 1024),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Linear(1024, 128 * 7 * 7),\n",
    "            nn.BatchNorm1d(128 * 7 * 7),\n",
    "            nn.LeakyReLU(0.2, inplace=True)\n",
    "        )\n",
    "\n",
    "        # 反卷积层（转置卷积层），用于将高维特征图逐步上采样为最终图像\n",
    "        self.deconv = nn.Sequential(\n",
    "            nn.ConvTranspose2d(128, 128, 4, 2, 1),  # 7x7 -> 14x14\n",
    "            nn.BatchNorm2d(128),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.ConvTranspose2d(128, 64, 4, 2, 1),   # 14x14 -> 28x28\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Conv2d(64, self.output_dim, 3, 1, 1),  # 保持尺寸不变，但细化特征\n",
    "            nn.Tanh()  # 激活函数，将输出值限制在 [-1, 1] 范围内，适合生成图像\n",
    "        )\n",
    " \n",
    "    def forward(self, noise, labels):\n",
    "        # 标签处理\n",
    "        label_embedding = self.label_emb(labels)\n",
    "        \n",
    "        # 拼接噪声和条件向量\n",
    "        x = torch.cat([noise, label_embedding], dim=1)\n",
    "        \n",
    "        # 通过全连接层\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        # 重塑为特征图\n",
    "        x = x.view(-1, 128, 7, 7)\n",
    "        \n",
    "        # 通过反卷积层生成图像\n",
    "        x = self.deconv(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "532a2f68-9e49-45bd-9419-c483c1bac15e",
   "metadata": {},
   "source": [
    "#### Discriminator \n",
    "判别器的目标是区分输入图像是真实的还是生成的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a66459d5-8040-44d1-9258-edf98b3a29ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn.utils.spectral_norm as spectral_norm\n",
    "\n",
    "class Discriminator(nn.Module):\n",
    "    def __init__(self, input_dim=1, output_dim=1, class_num=10):\n",
    "        '''\n",
    "        初始化判别网络\n",
    "        :param input_dim:输入通道数\n",
    "        :param output_dim:输出通道数\n",
    "        '''\n",
    "        super(Discriminator, self).__init__()\n",
    "        self.input_dim = input_dim\n",
    "        self.output_dim = output_dim\n",
    "        self.class_num = class_num\n",
    "\n",
    "        # 标签嵌入\n",
    "        self.label_emb = nn.Embedding(class_num, 28*28)\n",
    "\n",
    "        # 卷积层使用频谱归一化提高稳定性\n",
    "        self.conv = nn.Sequential(\n",
    "            nn.Conv2d(self.input_dim + 1, 64, 4, 2, 1),  # +1 是为了连接条件信息\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),  # 添加Dropout防止过拟合\n",
    "            \n",
    "            nn.utils.spectral_norm(nn.Conv2d(64, 128, 4, 2, 1)),  # 频谱归一化\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),\n",
    "            \n",
    "            nn.utils.spectral_norm(nn.Conv2d(128, 256, 3, 1, 1)),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),\n",
    "        )\n",
    "        \n",
    "        # 全连接层\n",
    "        self.fc = nn.Sequential(\n",
    "            nn.utils.spectral_norm(nn.Linear(256 * 7 * 7, 1024)),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),\n",
    "            nn.utils.spectral_norm(nn.Linear(1024, self.output_dim))\n",
    "        )\n",
    " \n",
    "    def forward(self, img, labels):\n",
    "        # 处理标签\n",
    "        batch_size, _, height, width = img.shape\n",
    "        label_embedding = self.label_emb(labels).view(batch_size, 1, height, width)\n",
    "        \n",
    "        # 连接图像和标签信息\n",
    "        x = torch.cat([img, label_embedding], dim=1)\n",
    "        \n",
    "        # 卷积层\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 全连接层\n",
    "        x = x.view(batch_size, -1)\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17dcd94d-780b-491a-b254-1bb6e2b3b64a",
   "metadata": {},
   "source": [
    "### 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7f876252-f84c-4843-bfba-115e97170115",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 初始化权重函数\n",
    "def weights_init(m):\n",
    "    classname = m.__class__.__name__\n",
    "    if classname.find('Conv') != -1:\n",
    "        nn.init.normal_(m.weight.data, 0.0, 0.02)\n",
    "    elif classname.find('BatchNorm') != -1:\n",
    "        nn.init.normal_(m.weight.data, 1.0, 0.02)\n",
    "        nn.init.constant_(m.bias.data, 0)\n",
    "    elif classname.find('Linear') != -1:\n",
    "        nn.init.normal_(m.weight.data, 0.0, 0.02)\n",
    "        nn.init.constant_(m.bias.data, 0)\n",
    "\n",
    "# 定义生成器和判别器\n",
    "generator = Generator(input_dim=100, output_dim=1, class_num=10).to(device)\n",
    "discriminator = Discriminator(input_dim=1, output_dim=1, class_num=10).to(device)\n",
    "\n",
    "# 应用权重初始化\n",
    "generator.apply(weights_init)\n",
    "discriminator.apply(weights_init)\n",
    "\n",
    "# 使用Adam优化器，beta参数适合GAN训练\n",
    "optimizer_G = torch.optim.Adam(generator.parameters(), lr=0.0001, betas=(0.5, 0.999))\n",
    "optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))\n",
    "\n",
    "\n",
    "# 使用Wasserstein GAN with Gradient Penalty (WGAN-GP)损失函数\n",
    "def compute_gradient_penalty(D, real_samples, fake_samples, labels):\n",
    "    \"\"\"计算梯度惩罚项\"\"\"\n",
    "    # 随机权重项\n",
    "    alpha = torch.rand(real_samples.size(0), 1, 1, 1).to(device)\n",
    "    \n",
    "    # 在真实样本和生成样本之间进行插值\n",
    "    interpolates = (alpha * real_samples + ((1 - alpha) * fake_samples)).requires_grad_(True)\n",
    "    \n",
    "    # 计算插值样本的判别器输出\n",
    "    d_interpolates = D(interpolates, labels)\n",
    "    \n",
    "    # 获取梯度\n",
    "    gradients = torch.autograd.grad(\n",
    "        outputs=d_interpolates,\n",
    "        inputs=interpolates,\n",
    "        grad_outputs=torch.ones_like(d_interpolates),\n",
    "        create_graph=True,\n",
    "        retain_graph=True,\n",
    "        only_inputs=True\n",
    "    )[0]\n",
    "    \n",
    "    # 梯度惩罚\n",
    "    gradients = gradients.view(gradients.size(0), -1)\n",
    "    gradient_penalty = ((gradients.norm(2, dim=1) - 1) ** 2).mean()\n",
    "    \n",
    "    return gradient_penalty"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "338ab99f-9789-483d-ab7e-4b7926c4fbaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(dataloader, generator, discriminator, optimizer_G, optimizer_D, epoch):\n",
    "    generator.train()\n",
    "    discriminator.train()\n",
    "    running_loss_G = 0.0\n",
    "    running_loss_D = 0.0\n",
    "    \n",
    "    lambda_gp = 10  # 梯度惩罚权重\n",
    "    \n",
    "    progress_bar = tqdm(dataloader, desc=\"Training\", leave=False)\n",
    "    for index, (images, labels) in enumerate(progress_bar):\n",
    "        # 将数据移动到设备\n",
    "        batch_size = images.size(0)\n",
    "        images = images.to(device)\n",
    "        labels = labels.to(device)\n",
    "        \n",
    "        # ---------------------\n",
    "        #  训练判别器\n",
    "        # ---------------------\n",
    "        optimizer_D.zero_grad()\n",
    "        \n",
    "        # 生成随机噪声\n",
    "        z = torch.randn(batch_size, generator.input_dim).to(device)\n",
    "        \n",
    "        # 生成假图像\n",
    "        fake_images = generator(z, labels)\n",
    "        \n",
    "        # 计算真实和假图像的判别器输出\n",
    "        real_validity = discriminator(images, labels)\n",
    "        fake_validity = discriminator(fake_images.detach(), labels)\n",
    "        \n",
    "        # 梯度惩罚项\n",
    "        gradient_penalty = compute_gradient_penalty(\n",
    "            discriminator, images.data, fake_images.data, labels\n",
    "        )\n",
    "        \n",
    "        # 判别器损失 (WGAN-GP)\n",
    "        d_loss = -torch.mean(real_validity) + torch.mean(fake_validity) + lambda_gp * gradient_penalty\n",
    "        \n",
    "        # 更新判别器\n",
    "        d_loss.backward()\n",
    "        optimizer_D.step()\n",
    "        \n",
    "        running_loss_D += d_loss.item()\n",
    "        \n",
    "        # ---------------------\n",
    "        #  训练生成器\n",
    "        # ---------------------\n",
    "        # 每n_critic次更新一次生成器\n",
    "        n_critic = 5\n",
    "        if index % n_critic == 0:\n",
    "            optimizer_G.zero_grad()\n",
    "            \n",
    "            # 生成假图像\n",
    "            fake_images = generator(z, labels)\n",
    "            \n",
    "            # 判别器对假图像的评估\n",
    "            fake_validity = discriminator(fake_images, labels)\n",
    "            \n",
    "            # 生成器损失\n",
    "            g_loss = -torch.mean(fake_validity)\n",
    "            \n",
    "            # 更新生成器\n",
    "            g_loss.backward()\n",
    "            optimizer_G.step()\n",
    "            \n",
    "            running_loss_G += g_loss.item()\n",
    "            \n",
    "            # 更新进度条\n",
    "            progress_bar.set_postfix(\n",
    "                epoch=epoch+1,\n",
    "                G_loss=g_loss.item(),\n",
    "                D_loss=d_loss.item()\n",
    "            )\n",
    "        \n",
    "    avg_loss_G = running_loss_G / (len(dataloader) // n_critic)\n",
    "    avg_loss_D = running_loss_D / len(dataloader)\n",
    "    \n",
    "    return avg_loss_G, avg_loss_D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c9a1c0ac-5c59-4f2f-96a2-41bbff0f7e15",
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate(dataloader, generator, discriminator, epoch):\n",
    "    generator.eval()\n",
    "    discriminator.eval()\n",
    "    running_loss_G = 0.0\n",
    "    running_loss_D = 0.0\n",
    "    \n",
    "    lambda_gp = 10  # 梯度惩罚权重\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        progress_bar = tqdm(dataloader, desc=\"Evaluating\", leave=True)\n",
    "        for images, labels in progress_bar:\n",
    "            # 将数据移动到设备\n",
    "            batch_size = images.size(0)\n",
    "            images = images.to(device)\n",
    "            labels = labels.to(device)\n",
    "            \n",
    "            # 生成随机噪声\n",
    "            z = torch.randn(batch_size, generator.input_dim).to(device)\n",
    "            \n",
    "            # 生成假图像\n",
    "            fake_images = generator(z, labels)\n",
    "            \n",
    "            # 计算真实和假图像的判别器输出\n",
    "            real_validity = discriminator(images, labels)\n",
    "            fake_validity = discriminator(fake_images, labels)\n",
    "            \n",
    "            # 判别器损失 (WGAN-GP)\n",
    "            d_loss = -torch.mean(real_validity) + torch.mean(fake_validity)\n",
    "            \n",
    "            # 生成器损失\n",
    "            g_loss = -torch.mean(fake_validity)\n",
    "            \n",
    "            # 统计损失\n",
    "            running_loss_G += g_loss.item()\n",
    "            running_loss_D += d_loss.item()\n",
    "            \n",
    "            # 更新进度条\n",
    "            progress_bar.set_postfix(\n",
    "                epoch=epoch+1,\n",
    "                G_loss=g_loss.item(),\n",
    "                D_loss=d_loss.item()\n",
    "            )\n",
    "    \n",
    "    avg_loss_G = running_loss_G / len(dataloader)\n",
    "    avg_loss_D = running_loss_D / len(dataloader)\n",
    "    \n",
    "    return avg_loss_G, avg_loss_D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3d14364b-5bfe-421b-84d6-55c7d54e71f7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 训练过程中生成图片方便查看训练效果\n",
    "def generate_images(generator, epoch, fixed_noise=None, fixed_labels=None):\n",
    "    generator.eval()\n",
    "    with torch.no_grad():\n",
    "        num_images = 100\n",
    "        num_classes = 10\n",
    "        \n",
    "        # 使用固定噪声和标签以便比较不同epoch的进展\n",
    "        if fixed_noise is None:\n",
    "            fixed_noise = torch.randn(num_images, generator.input_dim).to(device)\n",
    "        if fixed_labels is None:\n",
    "            # 每个数字类别生成10张图片\n",
    "            fixed_labels = torch.tensor([i for i in range(num_classes) for _ in range(num_images // num_classes)]).to(device)\n",
    "        \n",
    "        # 生成图片\n",
    "        fake_images = generator(fixed_noise, fixed_labels)\n",
    "        fake_images = fake_images.cpu().detach()\n",
    "        \n",
    "        # 可视化并保存图片\n",
    "        fig, axes = plt.subplots(10, 10, figsize=(10, 10))\n",
    "        fig.subplots_adjust(hspace=0.1, wspace=0.1)\n",
    "        \n",
    "        for i, ax in enumerate(axes.flat):\n",
    "            # 转换图像范围从[-1, 1]到[0, 1]\n",
    "            img = (fake_images[i].squeeze() + 1) / 2\n",
    "            ax.imshow(img, cmap='gray')\n",
    "            ax.axis('off')\n",
    "            \n",
    "            # 在每行的第一列显示类别标签\n",
    "            if i % 10 == 0:\n",
    "                ax.set_title(f'{fixed_labels[i].item()}', fontsize=8)\n",
    "        \n",
    "        plt.savefig(f'./data/epoch_{epoch}.png', bbox_inches='tight')\n",
    "        plt.close()\n",
    "        \n",
    "        return fixed_noise, fixed_labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d46b4ba4-bcbf-49c0-9c52-25b8ca7d4613",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.20it/s, D_loss=-4.11, G_loss=-24.1, epoch=1]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 1, Train_loss_G:-18.333, Train_loss_D:-8.840, Test_loss_G:-24.131, Test_loss_D:-3.791\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 24.53it/s, D_loss=-2.23, G_loss=-27.7, epoch=2]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 2, Train_loss_G:-27.745, Train_loss_D:-2.964, Test_loss_G:-27.502, Test_loss_D:-2.433\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.51it/s, D_loss=-2.28, G_loss=-24.1, epoch=3]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 3, Train_loss_G:-28.125, Train_loss_D:-2.190, Test_loss_G:-23.781, Test_loss_D:-2.352\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.11it/s, D_loss=-2.09, G_loss=-25.8, epoch=4]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 4, Train_loss_G:-27.218, Train_loss_D:-2.240, Test_loss_G:-25.919, Test_loss_D:-2.526\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.35it/s, D_loss=-1.46, G_loss=-28.6, epoch=5]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 5, Train_loss_G:-27.624, Train_loss_D:-1.984, Test_loss_G:-28.676, Test_loss_D:-1.854\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.16it/s, D_loss=-1.75, G_loss=-25.7, epoch=6]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 6, Train_loss_G:-27.395, Train_loss_D:-1.762, Test_loss_G:-25.889, Test_loss_D:-1.664\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.62it/s, D_loss=-1.81, G_loss=-23.8, epoch=7]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 7, Train_loss_G:-27.672, Train_loss_D:-1.401, Test_loss_G:-23.801, Test_loss_D:-1.310\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 24.96it/s, D_loss=-0.34, G_loss=-24.1, epoch=8]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 8, Train_loss_G:-25.088, Train_loss_D:-0.934, Test_loss_G:-23.751, Test_loss_D:-0.862\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 79/79 [00:03<00:00, 25.41it/s, D_loss=-0.948, G_loss=-19.1, epoch=9]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 9, Train_loss_G:-24.581, Train_loss_D:-0.949, Test_loss_G:-18.908, Test_loss_D:-0.949\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 79/79 [00:03<00:00, 25.93it/s, D_loss=-0.458, G_loss=-29.1, epoch=10]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:10, Train_loss_G:-22.621, Train_loss_D:-0.808, Test_loss_G:-29.520, Test_loss_D:-1.233\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 79/79 [00:03<00:00, 25.33it/s, D_loss=0.759, G_loss=-23.9, epoch=11]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:11, Train_loss_G:-22.179, Train_loss_D:-0.763, Test_loss_G:-23.721, Test_loss_D:-0.848\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 79/79 [00:03<00:00, 25.71it/s, D_loss=-0.0884, G_loss=-21.2, epoch=12]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:12, Train_loss_G:-22.371, Train_loss_D:-0.819, Test_loss_G:-21.297, Test_loss_D:-0.552\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████████| 79/79 [00:03<00:00, 25.10it/s, D_loss=0.314, G_loss=-29, epoch=13]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:13, Train_loss_G:-21.802, Train_loss_D:-0.723, Test_loss_G:-29.263, Test_loss_D:-0.777\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 79/79 [00:03<00:00, 25.51it/s, D_loss=-0.747, G_loss=-32.3, epoch=14]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:14, Train_loss_G:-24.842, Train_loss_D:-0.545, Test_loss_G:-32.331, Test_loss_D:-1.112\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 79/79 [00:03<00:00, 25.03it/s, D_loss=-1.44, G_loss=-22.4, epoch=15]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:15, Train_loss_G:-23.937, Train_loss_D:-0.592, Test_loss_G:-22.661, Test_loss_D:-0.229\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.37it/s, D_loss=1.67, G_loss=-25.3, epoch=16]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:16, Train_loss_G:-25.436, Train_loss_D:-0.577, Test_loss_G:-25.405, Test_loss_D:-0.057\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 79/79 [00:03<00:00, 25.31it/s, D_loss=-0.986, G_loss=-23.5, epoch=17]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:17, Train_loss_G:-22.314, Train_loss_D:-0.420, Test_loss_G:-23.858, Test_loss_D:-0.037\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████████| 79/79 [00:03<00:00, 24.93it/s, D_loss=1.15, G_loss=-19, epoch=18]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:18, Train_loss_G:-22.141, Train_loss_D:-0.514, Test_loss_G:-19.555, Test_loss_D:-0.150\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.47it/s, D_loss=2.04, G_loss=-24.4, epoch=19]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:19, Train_loss_G:-21.054, Train_loss_D:-0.464, Test_loss_G:-24.186, Test_loss_D:-0.122\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 79/79 [00:03<00:00, 25.88it/s, D_loss=0.779, G_loss=-28.5, epoch=20]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:20, Train_loss_G:-24.923, Train_loss_D:-0.323, Test_loss_G:-29.250, Test_loss_D:-0.404\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 79/79 [00:03<00:00, 25.24it/s, D_loss=-1.76, G_loss=-23.8, epoch=21]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:21, Train_loss_G:-25.556, Train_loss_D:-0.407, Test_loss_G:-24.232, Test_loss_D:-0.644\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.37it/s, D_loss=0.96, G_loss=-28.5, epoch=22]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:22, Train_loss_G:-26.110, Train_loss_D:-0.311, Test_loss_G:-28.434, Test_loss_D:-0.317\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 79/79 [00:03<00:00, 25.29it/s, D_loss=-0.547, G_loss=-27, epoch=23]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:23, Train_loss_G:-26.083, Train_loss_D:-0.220, Test_loss_G:-27.577, Test_loss_D:-0.453\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 79/79 [00:03<00:00, 25.24it/s, D_loss=0.112, G_loss=-26.3, epoch=24]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:24, Train_loss_G:-26.744, Train_loss_D:-0.333, Test_loss_G:-26.535, Test_loss_D:-0.120\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████████| 79/79 [00:03<00:00, 25.30it/s, D_loss=0.222, G_loss=-30, epoch=25]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:25, Train_loss_G:-29.136, Train_loss_D:-0.251, Test_loss_G:-30.192, Test_loss_D:-0.147\n",
      "训练完成!\n"
     ]
    }
   ],
   "source": [
    "# 开始训练\n",
    "num_epochs = 25\n",
    "train_loss_G = []\n",
    "train_loss_D = []\n",
    "test_loss_G = []\n",
    "test_loss_D = []\n",
    "\n",
    "# 创建固定噪声和标签用于图像生成\n",
    "fixed_noise = torch.randn(100, generator.input_dim).to(device)\n",
    "fixed_labels = torch.tensor([i for i in range(10) for _ in range(10)]).to(device)\n",
    "\n",
    "# 学习率调度器\n",
    "scheduler_G = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_G, mode='min', factor=0.5, patience=5, verbose=True)\n",
    "scheduler_D = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_D, mode='min', factor=0.5, patience=5, verbose=True)\n",
    "\n",
    "# 训练循环\n",
    "for epoch in range(num_epochs):\n",
    "    # 训练\n",
    "    epoch_train_loss_G, epoch_train_loss_D = train(train_loader, generator, discriminator, optimizer_G, optimizer_D, epoch)\n",
    "    \n",
    "    # 在测试集上评估\n",
    "    epoch_test_loss_G, epoch_test_loss_D = evaluate(test_loader, generator, discriminator, epoch)\n",
    "    \n",
    "    # 学习率调度\n",
    "    scheduler_G.step(epoch_test_loss_G)\n",
    "    scheduler_D.step(epoch_test_loss_D)\n",
    "    \n",
    "    # 记录损失\n",
    "    train_loss_G.append(epoch_train_loss_G)\n",
    "    train_loss_D.append(epoch_train_loss_D)\n",
    "    test_loss_G.append(epoch_test_loss_G)\n",
    "    test_loss_D.append(epoch_test_loss_D)\n",
    "    \n",
    "    # 打印训练和测试结果\n",
    "    template = ('Epoch:{:2d}, Train_loss_G:{:.3f}, Train_loss_D:{:.3f}, Test_loss_G:{:.3f}, Test_loss_D:{:.3f}')\n",
    "    print(template.format(epoch+1, epoch_train_loss_G, epoch_train_loss_D, epoch_test_loss_G, epoch_test_loss_D))\n",
    "    \n",
    "    # 每 5 个 epoch 生成并保存图片\n",
    "    if (epoch + 1) % 5 == 0 or epoch == 0:\n",
    "        generate_images(generator, epoch + 1, fixed_noise, fixed_labels)\n",
    "\n",
    "\n",
    "print(\"训练完成!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0464f73e-cdf8-41ae-a3ff-5ca64fc8a295",
   "metadata": {},
   "source": [
    "### 结果可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "cfa7bec7-b7fe-4b38-bcab-d4cebf4800d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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nvvqq6/o2rUr3333Hc888wyNGjUiIiKi1McoSnHvo6TfCWtra4ACk+QUp2PHjly5ciXPfnft2mV8LocpvleF6dChAyqVynhcgKioKM6cOWMsw48//simTZsICAjgww8/JDg4GICNGzcCEBkZyaxZs7jvvvuYO3cu27dv59lnny33HLJCCGFKUteXvAxS1+cldb3U9TWNdBEXFc7NzY3MzEzWrl1L48aNWbt2Ld9//z3du3c3d9HysLOzY9WqVTz//PO4urqSkJCAj49PiX+U3dzcAENGzX79+vG///2PpUuXUrdu3RKXYdq0afTt25fZs2czZMgQvvrqK6Kiokr9Xlq0aEH9+vWJiorKN2WHm5sb//zzD7/99htpaWl88MEHhIeHk5WVVerjFKa491HS70SdOnVo2LAhX3zxBaNHj+bcuXN06dKlRN35nnzyST766COGDRvGwoULiY+P58UXX2T48OG0adPGZO81KyuLv/76K9/6zp074+vry/jx43nxxRfR6/XUr1+ft956i3r16jFhwgQA0tPTmT17NgDNmjXj0KFDJCUlGa+2Ozk58c0335CWlsaoUaNITExk9+7dFXo1XgghSkvqeqnrpa6Xul4YSIAtKtzIkSM5dOgQr7/+Olqtlr59+/Lmm2/yxRdfkJCQgKOjo7mLCMCoUaN47bXX2LdvHwkJCSiKgpubGz/99FOJThC6d+/Ou+++y5IlS1i8eDFdu3bl448/5oUXXiAsLAxfX99i99GnTx+++eYb3n33XVasWMGgQYN49NFHy/R+HnjgAX766Sc6dOiQZ/27775LVFQUI0aMwMPDg8cff5y6devyzz//lOk4BSnufZTmO/H9998zdepU1qxZg7u7O7/++muJymBtbc3u3buZMWMGTz75JNbW1jz11FMsWrTIZO8T4Pbt2wXOOxoYGEiHDh1YsWIF7u7uvP766yQnJzNgwAC2bt2Kg4MDYDg5iI6OZtGiRVy5cgU3NzfefPNNnn76aQBq1arFb7/9xquvvsojjzwCQM+ePVm8eLFJ34cQQpSH1PVS10tdL3W9MFApuQcoCHGPOn/+PC1btuSrr76iUaNGqFQqoqKieOGFFxg2bBiffPKJuYsohBBCiHKQul4IURkkwBYCQ9ef1157ja1btxIdHU1WVhZ169ZlwIABvPfee7i7u5u7iEIIIYQoB6nr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",
      "text/plain": [
       "<Figure size 1200x300 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "epochs_range = range(num_epochs)\n",
    "\n",
    "plt.figure(figsize=(12, 3))\n",
    "\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.plot(epochs_range, train_loss_G, label='Training Generator Loss')\n",
    "plt.plot(epochs_range, train_loss_D, label='Training Discriminator Loss')\n",
    "plt.legend(loc='upper right')\n",
    "plt.title('Training and Validation Loss')\n",
    "\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.plot(epochs_range, test_loss_G, label='Test Generator Loss')\n",
    "plt.plot(epochs_range, test_loss_D, label='Test Discriminator Loss')\n",
    "plt.legend(loc='upper right')\n",
    "plt.title('Training and Validation Loss')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf0e77fd-589a-4752-afde-a386b264e73a",
   "metadata": {},
   "source": [
    "### 模型保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "14d399db-c56a-446c-b8e9-19baadbcf4ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 指定保存路径\n",
    "save_dir = '../models/4_GAN_Image_Generator'\n",
    "\n",
    "# 确保目录存在，如果不存在则创建\n",
    "import os\n",
    "if not os.path.exists(save_dir):\n",
    "    os.makedirs(save_dir)\n",
    "\n",
    "# 保存模型\n",
    "torch.save(generator.state_dict(), os.path.join(save_dir, 'MINIST_generator.pth'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e13bdb3-1256-4f9f-a1a5-fd6265d9a808",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "807e427a-8bfb-4dde-a110-d5b4e5cede12",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
